﻿using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Log = Logger.Debug;

namespace Xiaowei.Services
{
    public interface ITTSPlayService
    {
        event Action<string> TTSPlayTimeout;
        event Action<string> TTSPlaying;
        event Action TTSPlayEnd;
        event Action TTSPlayFail;

        void Dispose();
        void Mute();

        void Unmute();

        Task<bool> PlayTTS(string url, string md5);
        void PauseTTS();
    }

    internal class TTSPlayService: ITTSPlayService
    {
        public event Action<string> TTSPlayTimeout;
        public event Action<string> TTSPlaying;
        public event Action TTSPlayEnd;
        public event Action TTSPlayFail;


        private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1);
        private MediaFoundationReader mf = null;
        private WaveOutEvent wo = null;
        private readonly object woLocker = new object();
        private string MD5;
        private FailsafeTimer failsafeTimer = new FailsafeTimer();

        public TTSPlayService()
        {
            Init();
        }

        private void Init()
        {
            failsafeTimer.FailsafeTimeout += () =>
            {
                Log.WriteLine($"TTSPlayer.FailsafeTimeout");
                //Dispose();

                //it seems that wo has been blocked and will never work in the furture. 
                //Playing must be sent. Or stopcapture will never be called at the side of UWP
                TTSPlayTimeout?.Invoke(MD5);


                Log.WriteLine($"TTSPlayer.FailsafeTimeout, end");
            };
        }

        private async Task CreateAndPlay(string url, string md5)
        {

            Log.WriteLine($"TTSPlayer.CreateAndPlay");
            MD5 = md5;
            lock (woLocker)
            {

                Log.WriteLine($"TTSPlayer.CreateAndPlay: new MediaFoundationReader");
                try
                {
                    Log.WriteLine($"TTSPlayer.CreateAndPlay: Get Lock, {md5}");
                    failsafeTimer.Start(5000);

                    Log.WriteLine($"TTSPlayer.CreateAndPlay: Delay end");
                    if (mf != null)
                    {
                        mf.Dispose();
                        mf = null;
                    }
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    mf = new MediaFoundationReader(url);
                    sw.Stop();
                    Log.WriteLine($"TTSPlayer.CreateAndPlay: new MediaFoundationReader, {md5}, elapsed:{sw.ElapsedMilliseconds}ms.");

                    if (wo == null)
                    {
                        wo = new WaveOutEvent();
                        Log.WriteLine($"TTSPlayer.CreateAndPlay: new WaveOutEvent, {md5}");
                    }
                    wo.Volume = 1.0f;
                    wo.PlaybackStopped += Wo_PlaybackStopped;
                    Log.WriteLine($"TTSPlayer.CreateAndPlay: Volume, {md5}");
                    wo.Init(mf);
                    Log.WriteLine($"TTSPlayer.CreateAndPlay: wo.Init, {md5}");
                    wo.Play();
                    Log.WriteLine($"TTSPlayer.CreateAndPlay: wo.Play, {md5}");
                }
                catch (Exception ex)
                {
                    Log.WriteLine($"TTSPlayer.CreateAndPlay: Exception:{ex}");
                    TTSPlayFail?.Invoke();
                    return;
                }
                finally
                {
                    failsafeTimer.Stop();
                }

            }

            TTSPlaying?.Invoke(md5);
            Log.WriteLine($"TTSPlayer.CreateAndPlay: SendMessageAsync {md5} finished");
        }

        public void Dispose()
        {
            lock (woLocker)
            {
                if (mf != null)
                {
                    mf.Dispose();
                    mf = null;
                }

                if (wo != null)
                {
                    wo.PlaybackStopped -= Wo_PlaybackStopped;
                    wo.Dispose();
                    wo = null;
                }

                if (semaphore.CurrentCount == 0)
                {
                    semaphore.Release();
                    Log.WriteLine($"semaphore.Release");
                }
                Log.WriteLine($"TTSPlayer.Disposed");
            }
        }

        private void Wo_PlaybackStopped(object sender, StoppedEventArgs e)
        {
            Log.WriteLine($"Wo_PlaybackStopped, Enter.");
            Dispose();

            TTSPlayEnd?.Invoke();

            Log.WriteLine($"Wo_PlaybackStopped, Exit.");
        }

        public void Mute()
        {
            lock (woLocker)
            {
                if (wo == null) return;
                Log.WriteLine($"TTSPlayer.Mute:{wo.Volume}");
                wo.Volume = 0;
            }
        }

        public void Unmute()
        {
            lock (woLocker)
            {
                if (wo == null) return;
                Log.WriteLine($"TTSPlayer.Unmute:{wo.Volume}");
                wo.Volume = 1.0f;
            }
        }

        public async Task<bool> PlayTTS(string url, string md5)
        {
            if (string.IsNullOrEmpty(url)) return false;
            Log.WriteLine($"TTSPlayer.PlayTTS, semaphore.Wait:{md5}");

            if (semaphore.Wait(3000))
            {
                Log.WriteLine($"TTSPlayer.PlayTTS, semaphore.Wait ok: {md5}");
                await CreateAndPlay(url, md5).ConfigureAwait(false);
                return true;
            }
            else
            {
                Log.WriteLine($"TTSPlayer.PlayTTS, semaphore.Wait fail: {md5}");
                Dispose();
                TTSPlayFail?.Invoke();
                return false;
            }
        }
        public void PauseTTS()
        {
            Log.WriteLine($"TTSPlayer.PauseTTS Enter.");
            Dispose();
            Log.WriteLine($"TTSPlayer.PauseTTS Exit.");
        }
    }
}
